package viewer;

import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;

import model.RoffVector3D;

import java.awt.*;
import java.awt.event.*;

public class RoffLightingPanel extends JPanel implements ChangeListener, MouseListener {

    private RoffPhong       illuminator;
    private RoffPerspective transformer;
    private RoffSpherePanel rsp;

    private JSlider shinySlider;
    private JSlider transSlider;
    private JSlider lightX;
    private JSlider lightY;
    private JSlider lightZ;
    private JSlider ambientRef;
    private JSlider diffuseRef;
    private JSlider specularRef;

    private JPanel  insideColor;
    private JPanel  outsideColor;
    private JPanel  highlightColor;
    private JPanel  ambientColor;
    private JPanel  directionalColor;


    public RoffLightingPanel(RoffPhong newPhong) {

        GridBagLayout gridBag;
        GridBagConstraints c;
        JLabel label;

        transformer = new RoffPerspective();

        transformer.setAt(new RoffVector3D(0,0,0));
        transformer.setFrom(new RoffVector3D(0,0,-2.5));
	transformer.setUp(new RoffVector3D(0,1.0,0));
	transformer.setAngle(90.0);

        illuminator = newPhong;
        rsp = new RoffSpherePanel(transformer, illuminator);

        insideColor      = createColorPanel(illuminator.getInsideColor());
        outsideColor     = createColorPanel(illuminator.getOutsideColor());
        highlightColor   = createColorPanel(illuminator.getHighlightColor());
        ambientColor     = createColorPanel(illuminator.getAmbientColor());
        directionalColor = createColorPanel(illuminator.getDirectionalColor());

        shinySlider = new JSlider(1, 30, (int)illuminator.getShininess());
        shinySlider.addChangeListener(this);
        shinySlider.setToolTipText("" + illuminator.getShininess());

        transSlider = new JSlider(0, 100, (int)((1.0 - illuminator.getTransparency()) * 100.0));
        transSlider.addChangeListener(this);
        transSlider.setToolTipText("" + (1.0 - illuminator.getTransparency()));

        RoffVector3D light = illuminator.getLightDirection();

        lightX = new JSlider(0, 20, (int)(light.x * 10.0) + 10);
        lightX.addChangeListener(this);
        lightX.setToolTipText("" + light.x);
        lightY = new JSlider(0, 20, (int)(light.y * 10.0) + 10);
        lightY.addChangeListener(this);
        lightY.setToolTipText("" + light.y);
        lightZ = new JSlider(0, 20, (int)(light.z * 10.0) + 10);
        lightZ.addChangeListener(this);
        lightZ.setToolTipText("" + light.z);

        ambientRef = new JSlider(0, 100, (int)(illuminator.getAmbientFactor() * 100.0));
        ambientRef.addChangeListener(this);
        ambientRef.setToolTipText("" + illuminator.getAmbientFactor());
        diffuseRef = new JSlider(0, 100, (int)(illuminator.getDiffuseFactor() * 100.0));
        diffuseRef.addChangeListener(this);
        diffuseRef.setToolTipText("" + illuminator.getDiffuseFactor());
        specularRef = new JSlider(0, 100, (int)(illuminator.getSpecularFactor() * 100.0));
        specularRef.addChangeListener(this);
        specularRef.setToolTipText("" + illuminator.getSpecularFactor());

        JPanel objPanel;
        JPanel lightPanel;
        JPanel colorPanel;
        JPanel previewPanel;

        objPanel = new JPanel();
        gridBag = new GridBagLayout();
        c = new GridBagConstraints();
        objPanel.setLayout(gridBag);
        objPanel.setBorder(BorderFactory.createTitledBorder(
                           BorderFactory.createEtchedBorder(),
                           "Object Surface Properties")
                          );

        c.gridy = 0;
        c.gridx = 0;
        label = new JLabel("Outside Color:");
        gridBag.setConstraints(label, c);
        objPanel.add(label);

        c.gridx = 1;
        c.insets = new Insets(0, 8, 4, 8);
        gridBag.setConstraints(outsideColor, c);
        objPanel.add(outsideColor);

        c.gridx = 2;
        c.insets = new Insets(0, 0, 0, 0);
        label = new JLabel("Inside Color:");
        gridBag.setConstraints(label, c);
        objPanel.add(label);

        c.gridx = 3;
        c.insets = new Insets(0, 8, 4, 8);
        gridBag.setConstraints(insideColor, c);
        objPanel.add(insideColor);

        c.gridx = 4;
        c.insets = new Insets(0, 0, 0, 0);
        label = new JLabel("Highlight Color:");
        gridBag.setConstraints(label, c);
        objPanel.add(label);

        c.gridx = 5;
        c.insets = new Insets(0, 8, 4, 0);
        gridBag.setConstraints(highlightColor, c);
        objPanel.add(highlightColor);

        c.insets = new Insets(0, 0, 0, 0);
        c.gridwidth = 3;
        c.gridy = 1;
        c.gridx = 0;
        label = new JLabel("Shininess:");
        gridBag.setConstraints(label, c);
        objPanel.add(label);

        c.gridx = 3;
        gridBag.setConstraints(shinySlider, c);
        objPanel.add(shinySlider);

        c.gridy = 2;
        c.gridx = 0;
        label = new JLabel("Transparency:");
        gridBag.setConstraints(label, c);
        objPanel.add(label);

        c.gridx = 3;
        gridBag.setConstraints(transSlider, c);
        objPanel.add(transSlider);

        c.gridy = 3;
        c.gridx = 0;
        label = new JLabel("Ambient Reflectivity:");
        gridBag.setConstraints(label, c);
        objPanel.add(label);

        c.gridx = 3;
        gridBag.setConstraints(ambientRef, c);
        objPanel.add(ambientRef);

        c.gridy = 4;
        c.gridx = 0;
        label = new JLabel("Diffuse Reflectivity:");
        gridBag.setConstraints(label, c);
        objPanel.add(label);

        c.gridx = 3;
        gridBag.setConstraints(diffuseRef, c);
        objPanel.add(diffuseRef);

        c.gridy = 5;
        c.gridx = 0;
        label = new JLabel("Specular Reflectivity:");
        gridBag.setConstraints(label, c);
        objPanel.add(label);

        c.gridx = 3;
        gridBag.setConstraints(specularRef, c);
        objPanel.add(specularRef);
        
        JPanel lowerPanel = new JPanel();
        lowerPanel.setLayout(new BoxLayout(lowerPanel, BoxLayout.X_AXIS));

        lightPanel = new JPanel();
        lightPanel.setLayout(new GridLayout(0, 1));
        lightPanel.setBorder(BorderFactory.createTitledBorder(
                           BorderFactory.createEtchedBorder(),
                           "Light Properties")
                          );

        colorPanel = new JPanel();
        gridBag = new GridBagLayout();
        c  = new GridBagConstraints();
        colorPanel.setLayout(gridBag);

        c.gridx = 0;
        c.gridy = 0;
        c.gridwidth = 2;
        label = new JLabel("Ambient Light Color:");
        gridBag.setConstraints(label, c);
        colorPanel.add(label);

        c.gridx = 2;
        c.gridwidth = 1;
        gridBag.setConstraints(ambientColor, c);
        colorPanel.add(ambientColor);

        c.gridx = 0;
        c.gridy = 1;
        c.gridwidth = 2;
        label = new JLabel("Directional Light Color:");
        gridBag.setConstraints(label, c);
        colorPanel.add(label);

        c.gridx = 2;
        c.gridwidth = 1;
        gridBag.setConstraints(directionalColor, c);
        colorPanel.add(directionalColor);

        c.gridx = 0;
        c.gridy = 2;
        label = new JLabel("Left");
        gridBag.setConstraints(label, c);
        colorPanel.add(label);

        c.gridx = 1;
        gridBag.setConstraints(lightX, c);
        colorPanel.add(lightX);

        c.gridx = 2;
        label = new JLabel("Right");
        gridBag.setConstraints(label, c);
        colorPanel.add(label);

        c.gridx = 0;
        c.gridy = 3;
        label = new JLabel("Below");
        gridBag.setConstraints(label, c);
        colorPanel.add(label);

        c.gridx = 1;
        gridBag.setConstraints(lightY, c);
        colorPanel.add(lightY);

        c.gridx = 2;
        label = new JLabel("Above");
        gridBag.setConstraints(label, c);
        colorPanel.add(label);

        c.gridx = 0;
        c.gridy = 4;
        label = new JLabel("Front");
        gridBag.setConstraints(label, c);
        colorPanel.add(label);

        c.gridx = 1;
        gridBag.setConstraints(lightZ, c);
        colorPanel.add(lightZ);

        c.gridx = 2;
        label = new JLabel("Back");
        gridBag.setConstraints(label, c);
        colorPanel.add(label);

        lightPanel.add(colorPanel);


        previewPanel = new JPanel();
        previewPanel.setLayout(new GridLayout(0, 1));
        previewPanel.add(rsp);
        previewPanel.setBorder(BorderFactory.createTitledBorder(
                           BorderFactory.createBevelBorder(BevelBorder.LOWERED),
                           "Preview")
                          );

        lowerPanel.add(lightPanel);
        lowerPanel.add(previewPanel);

        this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        this.add(objPanel);
        this.add(lowerPanel);
    }

    public RoffPhong getLightingModel() {
        return illuminator;
    }

    private JPanel createColorPanel(Color newColor) {
        JPanel rval = new JPanel();
        rval.setBackground(newColor);
        rval.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
        rval.addMouseListener(this);
        rval.setPreferredSize(new Dimension(40, 15));
        return rval;
    }

    public void stateChanged(ChangeEvent e) {
        boolean redraw = false;

        if (e.getSource() == shinySlider) {
             illuminator.setShininess((double)shinySlider.getValue());
             shinySlider.setToolTipText("" + illuminator.getShininess());
             redraw = true;
        } else if (e.getSource() == transSlider) {
             illuminator.setTransparency((double)(100 - transSlider.getValue()) / 100.0);
             transSlider.setToolTipText("" + (1.0 - illuminator.getTransparency()));
             redraw = true;
        } else if (e.getSource() == ambientRef) {
             illuminator.setAmbientFactor((double)ambientRef.getValue() / 100.0);
             ambientRef.setToolTipText("" + illuminator.getAmbientFactor());
             redraw = true;
        } else if (e.getSource() == diffuseRef) {
             illuminator.setDiffuseFactor((double)diffuseRef.getValue() / 100.0);
             diffuseRef.setToolTipText("" + illuminator.getDiffuseFactor());
             redraw = true;
        } else if (e.getSource() == specularRef) {
             illuminator.setSpecularFactor((double)specularRef.getValue() / 100.0);
             specularRef.setToolTipText("" + illuminator.getSpecularFactor());
             redraw = true;
        } else if ((e.getSource() == lightX) ||
                   (e.getSource() == lightY) ||
                   (e.getSource() == lightZ)
                  ) {
            RoffVector3D newLight = new RoffVector3D((lightX.getValue() - 10) / 10.0,
                                                 (lightY.getValue() - 10) / 10.0,
                                                 (lightZ.getValue() - 10) / 10.0);
            if (newLight.getLength() == 0) {
                newLight = new RoffVector3D(0, 0, -1);
            } else {
                newLight.normalize();
            }
            illuminator.setLightDirection(newLight);
            lightX.setToolTipText("" + newLight.x);
            lightY.setToolTipText("" + newLight.y);
            lightZ.setToolTipText("" + newLight.z);
            redraw = true;
        }
        if (redraw) {
            rsp.fullPaint();
        }
    }

    public void mouseClicked(MouseEvent e) {

        boolean redraw = false;

        if (e.getSource() == insideColor) {
            illuminator.setInsideColor(this.changeColor(insideColor));
            redraw = true;
        } else if (e.getSource() == outsideColor) {
            illuminator.setOutsideColor(this.changeColor(outsideColor));
            redraw = true;
        } else if (e.getSource() == highlightColor) {
            illuminator.setHighlightColor(this.changeColor(highlightColor));
            redraw = true;
        } else if (e.getSource() == ambientColor) {
            illuminator.setAmbientColor(this.changeColor(ambientColor));
            redraw = true;
        } else if (e.getSource() == directionalColor) {
            illuminator.setDirectionalColor(this.changeColor(directionalColor));
            redraw = true;
        }

        if (redraw) {
            rsp.fullPaint();
            repaint();
        }
    }

    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
    public void mousePressed(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}

    private Color changeColor(JPanel panel) {

        Color rval = panel.getBackground();

        Color newColor = JColorChooser.showDialog(this, "Choose New Color", rval);
        if (newColor != null) {
            panel.setBackground(newColor);
            rval = newColor;
        }

        return rval;
    }
}